package BusinessLogic;

import Domain.*;
import Domain.MySqlAccessors.MySqlRecommendationProxy;
import Persistance.Exceptions.UnableToPredictRankException;
import Persistance.MySqlUtilityManagment;
import Persistance.FilteringAlgorithmContext;
import Persistance.CollaborationAlgo;
import org.apache.log4j.Logger;

import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Date;

import BusinessLogic.Exceptions.DataAlreadyExistException;


public class RecommendationHandler implements IHandlers<Recommendation>, Serializable {
    static Logger logger  = Logger.getLogger("MovieSystem.BussinessLogic.RecommendationHandler");

    private MySqlRecommendationProxy m_recommendations;
    private FilteringAlgorithmContext filter;

    public RecommendationHandler(MySqlUtilityManagment sql, int m_algoParam) {
        m_recommendations = new MySqlRecommendationProxy(sql);
        if (m_algoParam == 0) {
            filter = new FilteringAlgorithmContext(new CollaborationAlgo(m_recommendations));
        }
        else {
            KMeansAdapter m_KMeans = new KMeansAdapter(m_recommendations,m_algoParam);
            filter = new FilteringAlgorithmContext(new KMeansStrategy(m_KMeans));
        }

    }

    public void add(Recommendation r) {
        logger.info("Add Recommendation to dataBase");
        try {
            m_recommendations.add(r);
        }
        catch (DataAlreadyExistException e) {
            e.printStackTrace();
        }
    }

    public void delete(Recommendation r) {
       //function will not be called/used
        logger.info("Delete Recommendation from dataBase");
        m_recommendations.remove(r);
    }

    public void delete(User user){
        for(Recommendation r : m_recommendations.getUserItems(user.getUsername()))
            delete(r);
    }

    public void delete(Movie movie){
        for(Recommendation r : m_recommendations.getMovieItems(movie.getName()))
            delete(r);
    }

    /**
     * @param movieKey  the movie Key
     * @param userKey   the user Key
     * @return the recommendation of user about this movie
     */
    public Recommendation get(String movieKey, String userKey)  {
        return m_recommendations.get(userKey, movieKey);
    }

    /**
     * this function collect all recommendations acosiated to username
     * @param   username    The user identifier
     * @return  list of all recommendation written by this user
     */
    public Collection<Recommendation> getUserRecommendations(String username){
        return m_recommendations.getUserItems(username);
    }

    /**
     * this function returns the number of recommendations for a user
     * @param   username    The user identifier
     * @return  list of all recommendation written by this user
     */
    public int getUserRecCount(String username){
        return m_recommendations.getUserRecCount(username);
    }

    /**
     * this function collect all recommendations acosiated to the movie
     * @param   moviename     The movie identifier
     * @return  list of all recommendation written on this movie
     */
    public Collection<Recommendation> getMovieRecommendations(String moviename){
        return m_recommendations.getMovieItems(moviename);
    }

    public double getPrediction(String movie, String username) throws UnableToPredictRankException {
        return filter.getPredictionRating(username,movie);
    }

    public void addMovieRecommendation(String username, String moviename, int rank, String freetext) {
        logger.info("add new recommendation for movie "+moviename+" by user "+username);
        Recommendation r = new Recommendation(username,moviename,freetext,rank);
        if (get(moviename,username) != null) {
            logger.info("updating previous recommendation for movie "+moviename+" by user "+username);
        }
        add(r);
    }

    public Collection<String> getRecommendedMovies(String user) {
        Collection<String> ans = new LinkedList<String>();

        Collection<Recommendation> userItems = m_recommendations.getUserItems(user);
        for(Recommendation r : userItems){
            ans.add(r.getMovieName());
        }

        logger.debug("Recommended Movies ("+user+") = "+ans.size());
        return ans;
    }

    public double getMovieRank(String movie) {
        return m_recommendations.getMovieRank(movie);
    }

    public Date getRecommendationDate(Recommendation r){
        return r.getRecommendationDate();
    }

    public int getRecCount(String user) {
        return m_recommendations.getUserItems(user).size();
    }

    public Collection<String> getMostRatedMovies(int count, boolean topDown) {
        return m_recommendations.getMostRatedMovies(count,topDown);
    }

    public String[] getUsersByMovieRating(String moviename, int rank, int start, int amount) {
        return m_recommendations.getUsersByMovieRating(moviename,rank,start,amount);
    }
    
    public Collection<String> getMoviesByOrderedRate(boolean highestRate) {
        return m_recommendations.getMoviesByOrderedRate(highestRate);
    }
    
    public int GetHighestRate(String movie){
    	return m_recommendations.getHighestRank(movie);
    }
    
    public int GetLowestRate(String movie){
    	return m_recommendations.getLowestRank(movie);
    }
}
